[#ftl]
require({paths:{"${project}":"../../${project}"}},["dojo/i18n!${project}/nls/ResourceMessage"], function(nlsStrings){
	console.log("GREETING:",nlsStrings.GREETING);
	window["nlsStrings"] = nlsStrings;
});

//console.log("navigator.userAgent=",navigator.userAgent);
//console.log("navigator.appVersion=",navigator.appVersion);

//console.log("dojo.locale:",dojo.locale);
if(!lang.exists("agile")){
	lang.setObject("agile",{},kernel.global);
}

[#include "extend.ftl"/]

[#include "../styleMixin.ftl"/] 

[#--
//agile.stylePrefix = "";
//if(has("webkit") != undefined){
//	agile.isWebKit = has("webkit");
//	agile.stylePrefix = "-webkit-";
//}else if(has("ie") != undefined){
//	agile.isIE = has("ie");
//	 if(agile.isIE == 9){
//		 agile.stylePrefix = "-ms-";
//	 }	
//}else if(has("mozilla") != undefined){	
//	agile.isMozilla = has("mozilla");
//	agile.stylePrefix = "-moz-";				
//}else if(has("opera") != undefined){
//	agile.isOpera = has("opera");
//	agile.stylePrefix = "-o-";
//}else if(navigator.appVersion.indexOf("rv:11") != -1){		
//	agile.isIE = 11;
//	agile.stylePrefix = "-ms-";
//}
--]	
[@mixin "agile"]	
//capitalize:function(text,excludeFirstWord){
//	var newText = "";
//	var words = text.split(" ");
//	for(var i = excludeFirstWord?1:0; i < words.length; i++){
//		newText += words[i].substring(0,1).toUpperCase() + words[i].substring(1);
//	}
//	if(excludeFirstWord){
//		newText = words[0] + newText;
//	}
//	return newText;
//}
[@styleMixin/]
[/@]
[@mixin "agile"]
	MSG:{	
		MSG_ERROR : "{0} -> Error occurred:"
		,MSG_METHOD_NOT_IMPLEMENTED : "[class:{0}] method '{1}' not implemented"
	    //,MSG_NULL_VALUE : "value of '{0}' is null [method:{1},class:{2}]"
	    //,MSG_BLANK_VALUE : "value of '{0}' is blank [method:{1},class:{2}]"
	    ,MSG_ASSERT_VALUE : "[class:{0},method:{1}] field '{2}' value is {3}"		    	
	    ,MSG_ASSERT_TYPE :  "{5} [class:{0},method:{1}] {2} : expected type is {3} instead of {4}"
	}
	//,zIndex:0
//	,customeCSS3:["transform","transform-origin","transition","keyframes","animation","animation-play-state"]
//	,adviseCssName4Html:function(htmlName){
//		var newName = htmlName;
//		for(var i = agile.customeCSS3.length - 1; i >= 0; i--){
//			if (htmlName == agile.customeCSS3[i]){ 
//				newName = agile.stylePrefix + htmlName;
//				break;
//			}
//		}
//		return newName;
//	}
//	,adviseCssRules:function(css){
//		if(this.customeCSS3Rexp == undefined){
//			this.customeCSS3Rexp = {};
//			for(var i = 0; i < this.customeCSS3.length; i++){
//				this.customeCSS3Rexp[this.customeCSS3[i]] = new RegExp(this.customeCSS3[i]+":","g");
//			}
//		}
//		//TODO:performance turning to avoid looping
//		var newCss =  css;
//		if(newCss){
//			for(var e in this.customeCSS3Rexp){
////				console.log("##############@@@@@@@@@",newCss,this.customeCSS3Rexp[e],agile.stylePrefix, e)
//				newCss = newCss.replace(this.customeCSS3Rexp[e],agile.stylePrefix + e + ":");
//			}
//		}
//		return newCss;
//	}
//	,adviseCssName4Js:function(htmlName){
//		var newName = htmlName;
//		var customized = false;
//		for(var i = agile.customeCSS3.length - 1; i >= 0; i--){
//			if (htmlName == agile.customeCSS3[i]){
//				newName = agile.stylePrefix.replace(/-/g,"") + agile.capitalize(htmlName.replace(/-/g," "),false).replace(/ /g,"");
//				customized = true;
//				break;
//			}
//		}
//		
//		if(!customized){
//			newName = agile.capitalize(htmlName.replace(/-/g," "),true).replace(/ /g,"");
//		}
//		////console.log("@@@@@@@@@",customized,name,newName);
//		return newName;
//	}
	,calcSize:function(domNode){		
		var box = geometry.getMarginBox(domNode);
		for(var e in box){
			box[e] = Math.round(box[e]);
		}		
		return box;
	}
	,coords:function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
		//	summary:
		//		Deprecated: Use position() for border-box x/y/w/h
		//		or marginBox() for margin-box w/h/l/t.
		//		Returns an object representing a node's size and position.
		//
		//	description:
		//		Returns an object that measures margin-box (w)idth/(h)eight
		//		and absolute position x/y of the border-box. Also returned
		//		is computed (l)eft and (t)op values in pixels from the
		//		node's offsetParent as returned from marginBox().
		//		Return value will be in the form:
		//|			{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }
		//		Does not act as a setter. If includeScroll is passed, the x and
		//		y params are affected as one would expect in dojo.position().
		var n = dom.byId(node), s = style.getComputedStyle(n), mb = geometry.getMarginBox(n, s);
		var abs = geometry.position(n, includeScroll);
		mb.x = abs.x;
		mb.y = abs.y;
		return mb;
	}
[/@]
[@mixin "agile"]
	i18n:function(/*String*/key,/*Array*/parameters,/*boolean|String*/defaultMsg){		
		var s = nlsStrings[key];
		if(s){
			if(parameters){
				var v = {};
				for(var i = 0; i < parameters.length; i++){
					v[""+i] = parameters[i];
				}
				s = lang.replace(s,v);
			}
		}else{			
			if (typeof defaultMsg == "string"){
				s = defaultMsg;
			}else{//use key as default message
				s = defaultMsg?key.replace(/_/g," "):"";
			}
		}
		return s;
	}
	[@method "i18nTooltip"/]
	,i18nTooltip:function(/*String*/key,/*Array*/parameters,/*boolean|String*/ defaultMsg){
		var s = nlsStrings[key+"_tooltip"];
		if(s){
			if(parameters){
				var v = {};
				for(var i = 0; i < parameters.length; i++){
					v[""+i] = parameters[i];
				}
				s = lang.replace(s,v)					
			}
			//line break (100 characters per line)
			var count = 80;
			var lines = s.split("\n");
//			//console.log("tooltips:",lines)
			s = "";
			for(var i = 0; i < lines.length; i++){
				if(lines[i].length <= count){
					s += lines[i];
				}else{					
					var breakIndex = lines[i].lastIndexOf(" ",count + 1);
					s += lines[i].substring(0,breakIndex) + "\n";					
					lines[i] = lines[i].substring(breakIndex);	
					i--;
					continue;
				}
				s += "\n";
			}	
		}else{
			//console.warn("i18n",key+"_tooltip");
			if (typeof defaultMsg == "string"){
				s = defaultMsg;
			}else{//use key as default message
				s = defaultMsg?key.replace(/_/g," "):"";
			}
		}
		return s;
	}
	/*[@method "i18nMap"/]*/
	,i18nMap:function(/*Array*/keys,/*Array*/ parameters,/*boolean|String*/ defaultMsg){
		var value,map = [];
		for(var i = 0; i < keys.length; i++){
			value = nlsStrings[keys[i]];	
			if(value){
				if(parameters){
					value = lang.replace(value,parameters);
				}
			}else{
				//console.warn("i18n",keys[i]);
				if (typeof defaultMsg == "string"){
					value = defaultMsg;
				}else{//use key as default message
					value = defaultMsg?keys[i]:"";	
				}				
			}
			map.push({"value":value});
		}
		return map;
	}
	[@method "i18nValues"/]
	,i18nValues:function(string,forObject){
		var values = {};
		if(string){
			var i18nKeys = string.match(/{i18n_[\w|_]+}/g);
			if(i18nKeys){
				for(var i = i18nKeys.length - 1; i >= 0; i--){
					var i18nKey = i18nKeys[i].substring(6,i18nKeys[i].length - 1);
					if(i18nKey.indexOf("_tooltip") != -1){
						values["i18n_"+i18nKey] = agile.i18nTooltip(i18nKey.replace("_tooltip",""),null,true);
					}else{
						values["i18n_"+i18nKey] = agile.i18n(i18nKey,null,true);
					}
				}
			}
			
			var propertyKeys = string.match(/{this_[\w|_]+}/g);
			if(propertyKeys){
				for(var i = propertyKeys.length - 1; i >= 0; i--){					
					var key = propertyKeys[i].substring(6,propertyKeys[i].length - 1);
					values["this_"+key] = forObject[key];
				}
			}			
		}
		return values;
	}
[/@]
[@mixin "agile"]
	assertError:function(/*String*/message,/*Array*/parameters){		
		var errorMsg = lang.replace(message,parameters);
		throw new Error(errorMsg);
	}
	
	,assertOverrideMethod:function(methodName,className){    		
		throw new Error(lang.replace(this.MSG.MSG_METHOD_NOT_IMPLEMENTED,[className,methodName]));
	}
	
	,assertNull:function(value,name,methodName,className){
		if(value == null || value == undefined){    			
			//throw new Error(lang.replace(this.MSG.MSG_NULL_VALUE,[name,methodName,className]));
			throw new Error(lang.replace(this.MSG.MSG_ASSERT_VALUE,[className,methodName,name,'null']));
		}
	}
	
	,assertBlank:function(value,name,methodName,className){
		if(value == undefined || value == null || lang.trim(value) == ""){    			
			//throw new Error(lang.replace(this.MSG.MSG_BLANK_VALUE,[name,methodName,className]));
			throw new Error(lang.replace(this.MSG.MSG_ASSERT_VALUE,[className,methodName,name,'null/blank']));
		}
	}    	

	,assertFalse:function(value,name,methodName,className){
		if(value == undefined || value == null || value == false){    			
			throw new Error(lang.replace(this.MSG.MSG_ASSERT_VALUE,[className,methodName,name,'null/false']));
		}
	}
	
	,assertTrue:function(value,name,methodName,className){
		if(value == true){    			
			throw new Error(lang.replace(this.MSG.MSG_ASSERT_VALUE,[className,methodName,name,value]));
		}
	}
	
	,assertType:function(value,name,type,methodName,className,message){
		if(type == "array" && lang.isArray(value)){//do nothing			
		}else{
			if(typeof value != type){    			
				throw new Error(lang.replace(this.MSG.MSG_ASSERT_TYPE,[className,methodName,name,type,typeof value,message]));
			}
		}		
	}
[/@]
[@mixin "agile"]	
	singoton:function(){		
		var className = arguments[0];				
		if(lang.exists(className)){
			this.assertError("{0} already exists ",[className]);
		}else{
			
			////console.log(lang.replace("creating singoton {0}",[className]));
			
//			if(!lang.isObject(body)){
//				this.assertError("invalid object");
//			}
			var mixin = {};
			for(var i = 1; i < arguments.length; i++){
				lang.mixin(mixin,arguments[i]);
			}
			//create the singoton 
			lang.setObject(className,mixin,kernel.global);
			
			//set the declaredClass
			lang.setObject(className +".declaredClass",className,kernel.global);
						
			//initialize the singoton by calling its constructor() method
			if(typeof agile.getObject(className+".constructor") == "function"){
				eval(className+".constructor()");
			}
			////console.log(lang.replace("singoton {0} has been created",[className]));
		}
	}
	
	,merge:function(){
		var target = arguments[0];		
		var source = null;
		for(var i = 1; i < arguments.length; i++){			
			source = arguments[i];
//			//console.log("source"+i,typeof source,source);
			if(typeof source != "object"){
//				console.error("object expected rather than", typeof source);
			}else if(source.constructor == Array){
//				console.error("object expected rather than Array");				
			}else if(source.constructor == Date){
//				console.error("object expected rather than Date");
			}else{
				for(var e in source){
//					//console.log("source:"+e,typeof source[e],source[e]);
					if(source[e] == null){
						target[e] = source[e];
					}else if(typeof source[e] != "object"){
//						//console.log("##@@@@#target:"+e,target);
						target[e] = source[e];						
//						//console.log("##@@@@#source:"+e,typeof source[e],source[e]);						
					}else if(source[e].constructor == Date){
						target[e] = source[e];
					}else if(source[e].constructor == Array){
						target[e] = source[e];
					}else{
						if(target[e] == null){
							target[e] = lang.mixin({},source[e]);
							//target[e] = source[e];
						}else{
							target[e] = lang.mixin(target[e],source[e]);
						}
					}
				}
			}			
		}
//		//console.log("merge arguments:",arguments,"target",target);
		return target;
	}
	
	,delegate:function(){		
		var target = lang.delegate(arguments[0],{});
				
		var mixin = null;
		for(var i = 1; i < arguments.length; i++){			
			mixin = arguments[i];			
			for(var e in mixin){
				if(target[e] == null){
					target[e] = lang.mixin({},mixin[e]);
				}else{
					target[e] = lang.mixin({},target[e],mixin[e]);
					//target[e] = this.merge({},target[e],mixin[e]);
				}
			}
		}
		return target;
	}
[/@]
 [@mixin "agile"]	
	/*get value of the attribute specified by name from the object, return null if not exist*/
	getValue:function(/*String*/name,/*Object*/object){
		if(name == null || lang.trim(name) == ""){
			this.throwError("name can not be empty");
		}
		
		var paths = name.split(".");
		var value = object;
		for (var i = 0; i < paths.length; i++){
			if(value == null) break;
			value = value[paths[i]];
		}
		return value;
	}
	,setValue:function(/*String*/name,/*Any*/value,/*Object*/object){
		lang.setObject(name,value,object);
	}
	
	,setObject:function(){
		var name = arguments[0];
		var mixin = {};
		for(var i = 1; i < arguments.length; i++){			
			lang.mixin(mixin,arguments[i]);
		}		
		mixin.declaredClass = name;
		lang.setObject(name,mixin,kernel.global);		
	}	
	,getObject:function(){
		var path = arguments[0];
		var context = kernel.global;
		if(arguments.length > 1){
			context = arguments[1]
		}		
		return lang.getObject(path,context);
	}
[/@]

[@mixin "agile"]
	activeKeys:[]
	,isKeyActive:function(keyCode){
		return agile.activeKeys[keyCode];
	}
	,isCtrlKeyActive:function(){
		return agile.activeKeys[keys.CTRL];
	}
	,isShiftKeyActive:function(){
		return agile.activeKeys[keys.SHIFT];
	}
	,stopKeyEvent:function(widget){
		widget.own(
			on(widget, "keyDown", function(evt){
				evt.stopPropagation();
				//_event.stop(evt);
			})
			,on(widget, "keyUp",function(evt){
				evt.stopPropagation();
				//_event.stop(evt);
			})
		);
	}
	,backspace:function(widget){
		widget.own(
			on(widget, "keyDown", function(evt){
				if(evt.keyCode != keys.CTRL){
					//evt.stopPropagation();
					_event.stop(evt);
				}
			})
			,on(widget, "keyUp",function(evt){
				if(evt.keyCode != keys.CTRL){
					//evt.stopPropagation();
					_event.stop(evt);
				}
			})
		);
	}
	,onEnterKeyPress:function(formWidget,callback){				
		formWidget.own(
			on(formWidget, "keyDown", function(evt){			
				if (evt.keyCode == keys.ENTER){
					if(callback) callback();
				}
			})
		);
	}
	,onEscKeyPress:function(formWidget,callback){				
		formWidget.own(
			on(formWidget, "keyDown", function(evt){			
				if (evt.keyCode == keys.ESC){
					if(callback) callback();
					
					formWidget.undo();					
				}
			})
		);
	}
	,onKeyPress:function(formWidget,callbackMap){		
		formWidget.own(
			on(formWidget, "keyDown", function(evt){
				for(var i = 0; i < callbackMap.length; i++){
					if (evt.keyCode == callbackMap.keyCode){
						callbackMap.callback();
					}
				}
			})
		);	
	}
	,stopInlineEditBox:function(inlineEditBox){
		aspect.after(inlineEditBox,"edit",function(){
			var editWidget = inlineEditBox.wrapperWidget.editWidget;
			if(!editWidget._initialized){
				agile.backspace(editWidget);
//				on(editWidget, "onKeyDown", this,function(evt){
//					if(evt.keyCode != keys.CTRL){
//						evt.stopPropagation();
//					}
//				});
//				on(editWidget, "onKeyUp", this,function(evt){
//					if(evt.keyCode != keys.CTRL){
//						evt.stopPropagation();
//					}
//				});				
				editWidget._initialized = true;
			}
		});
	}	
[/@]

[@mixin "agile"]	
	__hasResource:{}
	,requireX:function(widgetClass,force){

		if(!widgetClass){return false;}
//		if(force){
//			this.__hasResource[widgetClass] = false;			
//		}
		if(this.__hasResource[widgetClass]){
			return true;
		}

		if(widgetClass.indexOf("dojo") == 0 || widgetClass.indexOf("dijit") == 0){
			//dojo.require(widgetClass);
			widgetClass = widgetClass.replace(/\./g,"\/");
			if(this.__hasResource[widgetClass]){
				return true;
			}
//			console.log("loading@@ ",widgetClass);			
			window.require({async:false},[widgetClass],function(widget){
				var dummy = widget;				
			});
			
			this.__hasResource[widgetClass] = true;
			
			return true;
		}
		[#if applicationMode = "DEV"]
		var uri = "${webAppHost}${context}/${project}/require?widget=" + widgetClass;
		[#else]
		var uri = "${webAppHost}${context}/${project}/" + widgetClass + ".js";
		[/#if]
		///console.log("force@@ ",force);		
		if(!force && window["localStorage"] && window["localStorage"][widgetClass]){
			try{
				eval(window["localStorage"][widgetClass]);				
			}catch(e){
				topic.publish("agile.message","error",null,"Error occurred while initializing widget: " + widgetClass,e);
			}
		}else{				
			//console.log("request@@ ",uri);	
			request.get(uri,{handleAs:"text" ,sync:true ,preventCache:true})
		    	.then(function(resp,ioArgs){
					try{
						eval(resp);						
						[#if localStorage]
						if(window["localStorage"]){
							 window["localStorage"][widgetClass] = resp;
						}
						[/#if]
					}catch(e){
						topic.publish("agile.message","error",null,"Error occurred while initializing widget: " + widgetClass,e);
						console.error(e);
						console.log(resp);
					}	    				    				    		
				}
		        ,function(err){				
					topic.publish("agile.message","error",null,err.message+".Failed to load widget: " + widgetClass,err);
				}
		   	);
		}
		return this.__hasResource[widgetClass];
	}
	
	,requireClazz:function(domNode){
		query("[data-dojo-type]",domNode).forEach(function(node){
			var dojotype = attr.get(node,"data-dojo-type");				
			if(!agile.__hasResource[dojotype]){
				agile.requireX(dojotype);	
			}				
		});
		query("[dojotype]",domNode).forEach(function(node){
			var dojotype = attr.get(node,"dojotype");				
			if(!agile.__hasResource[dojotype]){
				agile.requireX(dojotype);	
			}				
		});
	}
	,load:function(target,context,onDownloadEnd){		
	[#if hasServerResource]	
			var uri = "${webAppHost}${context}/${project}/load?target="+encodeURIComponent(target);
			request.get(uri,{handleAs:"text",sync:true,preventCache:true)
		    	.then(function(data,ioArgs){
					try{							
						//context.onDownloadEnd(data);
						onDownloadEnd.call(context,data);
//						[#if localStorage]
//						if(window.localStorage){
//							 window.localStorage[target] = data;
//						}
//						[/#if]
					}catch(e){
						topic.publish("agile.message","error",null,"Error occurred while initializing widget: " + widgetClass,e);
					}	    				    				    		
				}
		        ,function(error, ioargs){
				      var message = "";
				      switch(ioargs.xhr.status){
				         case 404:
				           message = "The requested page was not found";
				           break;
				         case 500:
				           message = "The server reported an error.";
				           break;
				         case 407:
				           message = "You need to authenticate with a proxy.";
				           break;
				         default:
				           message = "Unknown error.";
				      }
				      topic.publish("agile.message","error",null,"Error occurred while loading source",null);
				}
		   	);
	[#else]		
//		console.log("@@@@@@@@@@target=",target,window.localStorage[target]);
		var content = "";
		if(window.ls && window.ls[target]){
			content = window.ls[target];			
		}
		onDownloadEnd.call(context,content);	
	[/#if]
	}
			
//	,unload:function(target){
//		var uri = _baseLoadURI +"?target="+target;
//		if(window.localStorage){
//			window.localStorage.removeItem(uri);
//		}
//		//TODO:remove from server
//	}	
	,i18nUrl:function(name){
		var content = "";
		request.get("${webAppHost}${context}/${project}/i18n?type=url&name="+name,{handleAs:"text",sync:true,preventCache:true})
	    	.then(function(url,ioArgs){
					content = "${webAppHost}${context}/" + url;	
				}
		        ,function(error, ioargs){				     
				    console.error(error,ioargs);
				}
	    	);
		return content;			
	}
//	,submitForm:function(form,callback){	
//		dojo.xhrPost({form:form,handleAs:"json",sync:true,preventCache:true
//	    	,load: function(response,ioArgs){
//	    		if(response){
//	    			var message = response.message;
//	    			if(message.indexOf("i18n_") == 0){
//	    				message = agile.i18n(message.substring(5),response.param,true);
//	    			}	    			
//	    			topic.publish("agile.message",["info",null,message,null,null,callback]);
//	    		}				
//			}
//	        ,error: function(error, ioargs){				     
//			    console.error(error,ioargs);
//			    topic.publish("agile.message",["fatal",null,agile.i18n("msg_submit_error",null,true),error]);
//			}
//	   	});
//	}
//	,submitData:function(url,data,callback){	
//		dojo.xhrPost({url:url,content:data,handleAs:"json",sync:true,preventCache:true
//	    	,load: function(response,ioArgs){
//	    		if(response){
//	    			var message = response.message;
//	    			if(message.indexOf("i18n_") == 0){
//	    				message = agile.i18n(message.substring(5),response.param,true);
//	    			}	    			
//	    			topic.publish("agile.message",["info",null,message,null,null,callback]);
//	    		}				
//			}
//	        ,error: function(error, ioargs){				     
//			    console.error(error,ioargs);
//			    topic.publish("agile.message",["fatal",null,agile.i18n("msg_submit_error",null,true),error]);
//			}
//	   	});
//	}	
	
[/@]
[@mixin "agile"]	
	_styleSheets:{}
	,createStyleSheet:function(identifier){
		 var styleSheet; 
		 if(agile.isIE){
			 styleSheet = document.createStyleSheet();
         }else{
	         var dynamicStyle = document.createElement('style');  
	         dynamicStyle.type = 'text/css';
	         document.getElementsByTagName('head')[0].appendChild(dynamicStyle);	         
	         styleSheet = dynamicStyle.sheet;	
         }
		 this._styleSheets[identifier] = styleSheet;
		 return styleSheet;
	}
	[@method "addCssRule"/]
	,addCssRule:function(cssRule,styleSheetIdentifier){
		var styleSheet = this._styleSheets[styleSheetIdentifier];
		if(!styleSheet){			
			styleSheet = this._styleSheets["_default"];
		}
//		console.log("addCssRule:",cssRule,styleSheet);
		styleSheet.insertRule(cssRule,styleSheet.cssRules.length);
		
//		var cssRules = styleSheet.cssRules;
//		var rule = cssRules[cssRules.length-1];
//		//console.log("@@@style rule=",typeof rule,rule);
	}
//	function addGlobalStyle(css) {
//	    var head, style;
//	    head = document.getElementsByTagName('head')[0];
//	    if (!head) { return; }
//	    style = document.createElement('style');
//	    style.type = 'text/css';
//	    style.innerHTML = css;
//	    head.appendChild(style);
//	}
	,replaceCssRule:function(selector,cssRule){
		var cssRules = this._styleSheets["_default"].cssRules;
		for(var i = 0; i < cssRules.length; i++){
//			//console.log("style selector" + i,cssRules[i].selectorText);
			if(cssRules[i].selectorText == selector){
				this._styleSheets["_default"].deleteRule(i);			
				break;
			}
		}
		var styleSheet = this._styleSheets["_default"];
		styleSheet.insertRule(selector + " {"+cssRule+"}",styleSheet.cssRules.length);
	}
	,replacePseudoRule:function(selector,cssRule){
		var cssRules = this._styleSheets["_default"].cssRules;
		for(var i = 0; i < cssRules.length; i++){
//			//console.log("style selector" + i,cssRules[i].selectorText);
			if(cssRules[i].selectorText == selector){
				this._styleSheets["_default"].deleteRule(i);			
				break;
			}
		}
		var styleSheet = this._styleSheets["_default"];
		styleSheet.insertRule(selector + " {"+cssRule+"}",styleSheet.cssRules.length);
	}
	,replaceKeyFrameRule:function(name,keyframes){
		var cssRules = this._styleSheets["_default"].cssRules;
		for(var i = 0; i < cssRules.length; i++){
//			//console.log("style selector" + i,cssRules[i].selectorText);
			if(cssRules[i].name == name){
				this._styleSheets["_default"].deleteRule(i);			
				break;
			}
		}
		var styleSheet = this._styleSheets["_default"];
		
//		var styleRule = "@"+this.adviseCssName4Html("keyframes") + " " + keyframes.name + " {";
//		for(var e in keyframes){
//			if(e != "name"){
//				styleRule += e.substring(1) + "% {" + keyframes[e] +"} ";
//			}
//		}
//		styleRule += "}";		
		var styleRule = this.keyframes2String(name,keyframes);
//		console.log("@@@@@keyframes styleRule=",styleRule,"keyframes=",keyframes);
		if(styleRule != ""){
			styleSheet.insertRule(styleRule,styleSheet.cssRules.length);
		}
		if(!this.keyframes){
			this.keyframes = {};
		}
		this.keyframes[name] = keyframes;
	}
//	,keyframes2String:function(name,keyframes){
//		var styleRule = "";
//		for(var e in keyframes){
//			//p0 -> 0%, p100-100%
//			styleRule += e.substring(1) + "% {" + keyframes[e] +"} ";			
//		}
//		if(styleRule != ""){
//			styleRule = "@"+this.adviseCssName4Html("keyframes") + " " + name + " {" + styleRule +"}";
//		}
//		
//		return styleRule;
//	}
	,removeKeyFrameRule:function(name){
		var cssRules = this._styleSheets["_default"].cssRules;
		for(var i = 0; i < cssRules.length; i++){
//			//console.log("style selector" + i,cssRules[i].selectorText);
			if(cssRules[i].name == name){
				this._styleSheets["_default"].deleteRule(i);			
				break;
			}
		}
	}
	,removeCssRule:function(selector,wildcard){
		var cssRules = this._styleSheets["_default"].cssRules;
		for(var i = cssRules.length - 1; i >=0 ; i--){
			if(cssRules[i].selectorText == selector){
				this._styleSheets["_default"].deleteRule(i);			
				break;
			}
		}
	}
[/@]
[@mixin "agile"]	
	toJson:function(obj,ignoreNull){
		//return json.stringify(object);
		var jtext = "";		
		var object = obj;		
		for(var e in object){
			var value = object[e];
			//if(e.indexOf("-") != -1 || e.indexOf(" ") != -1){//single quotes are invalid for keys and string values.
				e = '"'+e+'"';
			//}
			if(value == null || value == ""){
//				if(!ignoreNull){
//					jtext += "\""+ e +"\":null,";
//				}
			}else if(typeof value == "boolean"){
				jtext += e +":" + value +",";
			}else if(typeof value == "number"){
				jtext += e +":" + value +",";
			}else if(typeof value == "string"){
				jtext += e +":\"" + value.replace(/\n/g,"#").replace(/"/g,'\\"').replace(/'/g,"&#39;") +"\",";
			}else if(value.constructor == Date){
				if(value.getYear() == 70){
					value = value.toString().replace(/.*1970\s(\S+).*/,'T$1');
				}else{
					value = locale.format(value,{formatLength:"medium",selector:"date",datePattern:"yyyy-MM-dd"});	
				}
				jtext += e +":\"" + value +"\",";
			}else if(value.constructor == Array){
				jtext += e +":[";
//				console.error("agile.tojtext(),array is not supported yet.",value);
				if(value.length > 0){
					jtext += agile.toJson(value[0],ignoreNull);
					for(var i = 1; i < value.length; i++){
						if(value[i]){
							jtext += ","+agile.toJson(value[i],ignoreNull);
						}
					}
				}			
				jtext += "],";	
			}else if(value.constructor == Object){			
				var js = agile.toJson(value,ignoreNull);
				if(js != "{}"){
					jtext += e+":"+ js +",";
				}
				//jtext += e+":"+agile.toJson(value,ignoreNull) +",";
			}
			else{
				var js = agile.toJson(value,ignoreNull);
				if(js != "{}"){
					jtext += e+":"+ js +",";
				}
				//jtext += e+":"+agile.toJson(value,ignoreNull) + ",";
			}				
		}
//		//console.log("@@@@@json:",json);
		
		jtext = "{"+jtext.substring(0,jtext.lastIndexOf(","))+"}";
				
		return jtext;
	}
//	,narrowNull:function(obj){
//		var value = {}
//		
//		for(var e in obj){
//			if(obj[e]){
//				if(lang.isArray(obj[e])){
//					value[e] = [];
//					for(var i = 0; i < obj[e].length; i++){
//						if(obj[e][i]){
//							value[e].push(this.narrowNull(obj[e][i]));
//						}
//					}
//				}else if(lang.isObject(obj[e])){
//					value[e] = this.narrowNull(obj[e]);
//				}else{
//					value[e] = obj[e];
//				}
//			}
//		}
//		return value;
//	}
[/@]

[@extend "dijit/_WidgetBase"]
	reset1:function(name,newValue){
		//TODO:remove this method
		if(newValue == undefined){
			newValue = null;
		}
		if(name == "value"){			
			this._created = false;
			this.set(name,newValue);		
			this._created = true;
		}else{
			this.set(name,newValue);
		}
		if(this.metadata != null){
			this.metadata(name,newValue);
		}
	}	
	,name:""
[/@] 

//lang.extend(dijit.form._FormWidget,{
//	constructor:function(){
//		//console.log("@@@@@@@@@@@@",this);
//		agile.backspace(this);
//	}
//});

[@extend "dijit/InlineEditBox"]
	reset1:function(name,newValue){
		if(newValue == undefined){
			newValue = null;
		}
		if(name == "value"){
			this.value = newValue;
			if(!newValue){
				this.displayNode.innerHTML=this.noValueIndicator;
			}else{
				this.displayNode.innerHTML=newValue;
			}
			
		}else{
			this.set(name,newValue);
		}
		if(this.metadata != null){
			this.metadata(name,newValue);
		}
	}	
	,name:""
//	,resizeAxis:""
//	,_setResizeAxisAttr:function(){
//		this.own(
//				aspect.after(this,"edit",lang.hitch(this,function(){
//					if(this._setResizeAxis)	return; this._setResizeAxis = true;
////					console.log("#######this.width=",this.width,",this.height=",this.height);
//					var editWidget = this.wrapperWidget.editWidget;
//					if(this.width){
//						style.set(editWidget.domNode,"width",this.width);
//					}
//					if(this.height){
//						style.set(editWidget.domNode,"height",this.height);
//					}
//				}))
//		);
//		this.watch("width",lang.hitch(this,function(name,old,value){
//			this._setResizeAxis = false;
//			style.set(this.domNode,"width",value);
//		}));
//		this.watch("height",lang.hitch(this,function(name,old,value){
//			this._setResizeAxis = false;
//			style.set(this.domNode,"height",value);
//		}));
//	}
[/@] 

[@extend "dijit/form/CheckBox"]
	isValid:function(){
		return true;
	}
[/@]

[@extend "dijit/form/TextBox"]
	isValid:function(){
		return true;
	}
[/@]

[@extend "dojox/form/Uploader"]
	acceptedMIME:null
	,onBegin:function(files){
		if(this.acceptedMIME == null) return;
		if(!array.some(files,function(file){
			var name = file.name.toLowerCase();
			var type = name.substring(name.lastIndexOf(".") + 1);
			return array.some(this.acceptedMIME,function(mime){
				return mime == type;
			});
		},this)){
			alert("only file \"" + this.acceptedMIME +"\" supported");
			throw "only file \"" + this.acceptedMIME +"\" supported";			
		}		
	}	
[/@]

[#--/*
[@extend "dijit/form/ValidationTextBox"]
	options:null
	,startup:function(){
		if(this.options && !this._optionsAdded){
			this._optionsAdded = true;
			this.addOption(this.options);
		}		
	}
	,getOptions:function(){
		return query('option', this.store.root);
	}
	,removeOption:function(option){
		construct.destroy(option, this.store.root);
	}
	,addOption:function(options){
		//console.log("ValidationTextBox=",this.store,"options=",options)
		if(options instanceof Array){
			array.forEach(options,function(option){		
				if(option && !option.label){
					option.label = option.value;
				}
				//construct.place(lang.replace("<option value='{value}'>{label}</option>",option), this.store.domNode, 'last');
				this.store.data.push(option);
			},this);
			//this.startup();
		}
	}
[/@]
*/--]



[#--@extend "dijit/form/FilteringSelect"]	
	required:false
//	,getOptions:function(){
//		return query('option', this.store.root);
//	}
//	,removeOption:function(option){
//		construct.destroy(option, this.store.root);
//	}
	,removeValue:function(value){
		var option = this.getOption(value);
		if(option){
			construct.destroy(option, this.store.root);
		}
	}
//	,addOption:function(options){				
//		var optionHtml = "<option value='{value}'>{label}</option>";				
//		array.forEach(options,function(option){		
//			if(!option.label){
//				option.label = option.value;
//			}
//			construct.place(lang.replace(optionHtml,option), this.store.root, 'last');	
//		},this);
//	}
	,hasOption:function(value){
		return query('option[value="'+value+'"]', this.store.root).length > 0;
	}
	,getOption:function(value){
		return query('option[value="'+value+'"]', this.store.root)[0];
	}
[/@--]


[@mixin "agile"]
	commits:[],maxUndoCount:20
	,commit:function(operations){
		/*
		 * Summary: keep audit for user opperations
		 * operation:{
		 * 		action:update,add,remove,undo
		 * 		,target: the widget or dom that is changed
		 *      ,category: style or parameters
		 * 		,property: property that is changed (for update only)
		 * 		,beforeValue: the value before change (for update only)
		 * 		,afterValue: the value after change (for update only)
		 * 		,context : the context of the callback (for undo only)
		 * 		,callback : the function to call (for undo only)
		 * }
		 */	
//		console.log(this.commits.length,"@@1",this.commits);
		this.commits.unshift(operations);		
		//destory the obsolute operations by remove it from the undo list
		if(this.commits.length > this.maxUndoCount){
			var commits = this.commits.splice(this.maxUndoCount);			
			for(var k = commits.length - 1; k >= 0; k--){	
				var undos = commits[k];
				if(undos == undefined) return;
				for(var i = undos.length - 1; i >= 0; i--){			
					//console.log("@@@@undo:",undos[i]);			
					var target = undos[i].target;
					[#--if(undos[i].action == "update"){
						
					}else if(undos[i].action == "add"){
						
					}else--] if(undos[i].action == "delete"){	
						undos[i].target.destroyRecursive(true);							
					}[#--else if(undos[i].action == "undo"){	
						
					}--]					
				}			
				undos.splice(0);
				undos = null;
			}
			commits = null;
		}
//		console.log(this.commits.length,"@@2",this.commits);		
	}	
	,undo:function(){
		var operations = this.commits.shift();
		if(operations == undefined) return;
		for(var i = 0; i < operations.length; i++){			
			console.log("undo:",operations[i]);			
			var target = operations[i].target;
			if(operations[i].action == "update"){
				if(operations[i].category == "style"){
					target.style1(operations[i].property,operations[i].beforeValue);
				}else if(operations[i].category == "parameters"){
					target.attributes[operations[i].property].onValueChange(target,operations[i].property,operations[i].beforeValue);
				}
			}else if(operations[i].action == "add"){
				if(target.parent){
					target.parent.removeChild(target);
				}
			}else if(operations[i].action == "delete"){
				if(target.parent){
					target.parent.addComponent(target);
				}
			}else if(operations[i].action == "undo"){
				if(operations[i].callback){
					operations[i].callback.apply(operations[i].context);
				}
			}
		}
	}
	,redo:function(){
		
	}
[/@]

agile.createStyleSheet("_default");	

[#include "MetadataConstraint.ftl"/]
[#include "Metadata.ftl"/]
[#include "StyleMetadata.ftl"/]
[#include "EventMetadata.ftl"/]
[#include "_PinAdvice.ftl"/]
[#include "_MetadataMixin.ftl"/]
[#include "_i18nTemplateMixin.ftl"/]
[#include "_Contained.ftl"/] 
[#include "_DataStore.ftl"/]

[#include "_ContainerMoveCoordinatorMixin.ftl"/] 
[#include "_ContainerSelectorMixin.ftl"/]
 
[#include "_ContainerToolbar.ftl"/]
[#include "_Container.ftl"/]
[#include "_ContainerToolbar2.ftl"/]
[#include "_Resizable.ftl"/]
[#include "_Moveable.ftl"/]
[#include "_Component.ftl"/]

[#include "layout/_LayoutWidget.ftl"/]
[#include "layout/_ContentPane.ftl"/]
[#include "layout/_StackContainer.ftl"/]

[#include "MessageDialog.ftl"/]
[#include "WidgetStoreDialog.ftl"/]
[#include "Application.ftl"/]

 